package ast2

import (
	"fmt"
	"strings"

	"gitee.com/u-language/u-language/ucom/config"
	"gitee.com/u-language/u-language/ucom/data"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/lex2"
)

type Tree struct {
	lex lex2.FileToken
	//包名
	PackageName string
	//文件名称
	Filename string
	//所有节点
	Nodes []Node
	//错误处理上下文
	errctx *errcode.ErrCtx
	//符号表
	Sbt *Sbt
	//变量初始化表
	VarInitTable *VarInitTable
	varInitTable VarInitTable
	//HaveInitFunc 记录是否有自定义init函数
	HaveInitFunc     *bool
	fileHaveInitFunc bool
	//C语言头文件用到的节点
	CHeaderFile *data.Slice[CDecl]
	cHeaderFile data.Slice[CDecl]
	//自己及依赖导入的包
	ImportPackage *map[string]*Package
	importPackage map[string]*Package
	//是所有的导入路径
	ImporPath        *Sbt
	originalSbtIndex int
	originalSbt      []*Sbt
	//下面两个字段用于确定是否在函数内解析
	Funcinfo *FuncInfo
	leftNum  int

	//inSwitch确定是否在switch代码块内解析
	inSwitch stackData
	//inFor确定是否在for代码块内解析
	inFor stackData

	//CheckInfo是专门在语义检查时使用的信息
	CheckInfo struct {
		//switch表达式的类型
		SwitchExprTyp string
		//是否要跳过switch代码块
		SwitchSkip bool
		//检查的符号是否是常量
		IsConst bool
		//一个函数内的标签
		Label []*LabelNode
		//一个函数内待检查的goto语句
		Goto []*GotoStmt
		//生成的临时变量数量
		TmpInt int
	}
}

func NewTree(l lex2.FileToken, path string, errctx *errcode.ErrCtx, thread bool) *Tree {
	//Note:因为目前单个文件不并发解析，所以thread目前没有用处
	ret := &Tree{
		lex:         l,
		errctx:      errctx,
		Sbt:         NewSbt(false),
		Filename:    path,
		originalSbt: make([]*Sbt, 1),
		ImporPath:   NewSbt(false),
	}
	ret.HaveInitFunc = &ret.fileHaveInitFunc
	ret.CHeaderFile = &ret.cHeaderFile
	ret.VarInitTable = &ret.varInitTable
	ret.ImportPackage = &ret.importPackage
	ret.importPackage = map[string]*Package{}
	ret.Parser()
	return ret
}

func (t *Tree) newTree(sbt *Sbt, lex lex2.FileToken, errctx *errcode.ErrCtx, HaveInitFunc *bool,
	VarInitTable *VarInitTable, CHeaderFile *data.Slice[CDecl], ImportPackage *map[string]*Package,
	ImporPath *Sbt) {
	t.Filename = lex.File
	t.errctx = errctx
	t.HaveInitFunc = HaveInitFunc
	t.Sbt = sbt
	t.VarInitTable = VarInitTable
	t.CHeaderFile = CHeaderFile
	t.ImportPackage = ImportPackage
	t.lex = lex
	t.ImporPath = ImporPath
}

func (t *Tree) String() string {
	var buf strings.Builder
	if config.Debug_PrintLex == "on" {
		buf.WriteString("lex:\n")
		buf.WriteString(t.lex.String())
	}
	if config.Debug_PrintAst == "on" {
		buf.WriteString(fmt.Sprintf("\nPackage:%s\tFileName:%s\n", t.PackageName, t.Filename))
		buf.WriteString(fmt.Sprintf("Sbt:%s\n", t.Sbt))
		buf.WriteString("\nNodes:\n")
		for i := range t.Nodes {
			buf.WriteString(fmt.Sprintf("%d:\t%s\n", i, t.Nodes[i].String()))
		}
	}
	return buf.String()
}

type stackData struct {
	v []bool
}

func (s *stackData) Push() {
	s.v = append(s.v, true)
}

func (s *stackData) Get() bool {
	return len(s.v) != 0
}

func (s *stackData) Pop() {
	if len(s.v) == 0 {
		return
	}
	s.v = s.v[: len(s.v)-1 : cap(s.v)]
}
